package Utils.Freebase;

import java.io.BufferedReader;
import java.io.FileReader;
import java.util.HashMap;
import java.util.Map;

import datatypes.film.Film;
import datatypes.film.FilmBudget;
import datatypes.film.filmGenre;
import datatypes.film.filmGenres;
import datatypes.film.filmRole;
import datatypes.film.Films;
import datatypes.film.filmActor;
import datatypes.film.filmActors;
import datatypes.film.filmDirector;
import datatypes.film.filmDirectors;
import datatypes.film.filmRoles;
import datatypes.film.filmWriter;
import datatypes.film.filmWriters;
import datatypes.tv.tvActor;
import datatypes.tv.tvActors;
import datatypes.tv.tvGenre;
import datatypes.tv.tvGenres;
import datatypes.tv.tvGuestRole;
import datatypes.tv.tvGuestRoles;
import datatypes.tv.tvProgram;
import datatypes.tv.tvProgramCreator;
import datatypes.tv.tvProgramCreators;
import datatypes.tv.tvPrograms;
import datatypes.tv.tvStarringRole;
import datatypes.tv.tvStarringRoles;

/**
 * FB_ParsingUtils handles all FreeBase(FB) parsing functionalities
 * 
 * all public parsing functions are designed so they can be executed
 * independently (using a thread pool for example)
 * 
 * @author micha
 * 
 */

public class FB_ParsingUtils {

	/* global variables */

	// pairs of (estimated budget mid id, estimated budget content)
	private static Map<String, FilmBudget> budgetMap = null;

	/**
	 * Initialize parsing utilities
	 */
	public static void init() {
		budgetMap = new HashMap<String, FilmBudget>();

	}

	/**
	 * @param tvActorfilePath
	 *            path to the actor .tsv file
	 * @param limit
	 *            limits the number of actors parsed (-1 for unlimited)
	 * 
	 * @return the list of all parsed actors
	 */
	public static tvActors Parse_tv_actor(String tvActorfilePath, int limit) {

		int limitCounter = 0;
		String line = null;
		tvActors tv_actors = new tvActors();

		// tv actor related members
		String actor_id = null;
		String name = null;
		String tv_starring_roles = null;
		String tv_guest_roles = null;

		try {
			BufferedReader bi = new BufferedReader(new FileReader(
					tvActorfilePath));
			bi.readLine(); // skip id definition
			while ((line = bi.readLine()) != null) {
				if (limitCounter == limit) {
					break;
				}

				String[] parts = line.split(FBUtils.FB_seperator, -1);

				name = parts[0];
				actor_id = parts[1];
				tv_starring_roles = parts[2];
				tv_guest_roles = parts[3];
				tv_actors.addActor(new tvActor(name, actor_id,
						tv_starring_roles, tv_guest_roles));
				limitCounter++;
			}

		} catch (Exception e) {
			System.out.println("error while parsing actors: " + e);
		}

		return tv_actors;

	}

	/**
	 * @param tvGuestRolesfilePath
	 *            path to the tv guest roles .tsv file
	 * @param limit
	 *            limits the number of guest roles (-1 for unlimited)
	 * 
	 * @return the list of all tvGuestRoles
	 */
	public static tvGuestRoles Parse_tv_guest_roles(
			String tvGuestRolesfilePath, int limit) {

		String line = null;

		// guest role related members
		String name;
		String gr_id;
		String actor;
		String character;
		String episodes_appeared_in;
		String special_performance_type;

		tvGuestRoles tv_guest_roles = new tvGuestRoles();

		String tv_gr_filePath = FBUtils.GetTVFilePathFromID("guest_roles");
		try {
			BufferedReader bi = new BufferedReader(new FileReader(
					tv_gr_filePath));
			bi.readLine(); // skip id definition
			while ((line = bi.readLine()) != null) {

				String[] parts = line.split(FBUtils.FB_seperator, -1);

				name = parts[0];
				gr_id = parts[1];
				actor = parts[2];
				character = parts[3];
				episodes_appeared_in = parts[4];
				special_performance_type = parts[5];

				tv_guest_roles.addGuestRole(new tvGuestRole(name, gr_id, actor,
						character, episodes_appeared_in,
						special_performance_type));
			}

		} catch (Exception e) {
			System.out.println("error while parsing actor guest roles: " + e);
		}
		return tv_guest_roles;

	}

	/**
	 * @param tvStarringRolesfilePath
	 *            path to the tv guest roles .tsv file
	 * @param limit
	 *            limits the number of guest roles (-1 for unlimited)
	 * 
	 * @return the list of all tvGuestRoles
	 */
	public static tvStarringRoles parse_regular_tv_appearance(
			String tvGuestRolesfilePath, int limit) {

		String line = null;

		// guest role related members
		String name;
		String sr_id;
		String actor;
		String character;
		String series;
		String seasons;

		tvStarringRoles tv_starring_roles = new tvStarringRoles();

		String tv_gr_filePath = FBUtils.GetTVFilePathFromID("starring_roles");
		try {
			BufferedReader bi = new BufferedReader(new FileReader(
					tv_gr_filePath));
			bi.readLine(); // skip id definition
			while ((line = bi.readLine()) != null) {

				String[] parts = line.split(FBUtils.FB_seperator, -1);

				name = parts[0];
				sr_id = parts[1];
				actor = parts[2];
				character = parts[3];
				series = parts[4];
				seasons = parts[8];
				tv_starring_roles.addGuestRole(new tvStarringRole(name, sr_id,
						actor, character, series, seasons));

			}

		} catch (Exception e) {
			System.out
					.println("error while parsing actor starring roles: " + e);
		}
		return tv_starring_roles;

	}

	/**
	 * @param tvProgramFilePath
	 *            path to the program .tsv file
	 * @param limit
	 *            limits the number of programs parsed (-1 for unlimited)
	 * 
	 * @return the list of all parsed programs
	 */
	public static tvPrograms Parse_tv_programs(String tvProgramFilePath,
			int limit) {
		int limitCounter = 0;
		String line = null;
		tvPrograms tv_programs = new tvPrograms();

		tvProgram tv_program = null;

		// program related members
		String name;
		String id;
		String program_creator;
		String air_date_of_first_episode;
		String air_date_of_final_episode;
		String number_of_episodes;
		String genre;
		String regular_cast;
		String number_of_seasons;

		try {
			BufferedReader bi = new BufferedReader(new FileReader(
					tvProgramFilePath));
			bi.readLine(); // skip id definition
			while ((line = bi.readLine()) != null) {
				if (limitCounter == limit) {
					break;
				}

				String[] parts = line.split(FBUtils.FB_seperator, -1);

				name = parts[0];
				id = parts[1];
				program_creator = parts[2];
				air_date_of_first_episode = parts[3];
				air_date_of_final_episode = parts[4];
				number_of_episodes = parts[5];
				genre = parts[10];
				regular_cast = parts[13];
				number_of_seasons = parts[19];

				tv_program = new tvProgram(name, id, program_creator,
						air_date_of_first_episode, air_date_of_final_episode,
						number_of_episodes, genre, regular_cast,
						number_of_seasons);
				tv_programs.addProgram(tv_program);

				limitCounter++;
			}

		} catch (Exception e) {
			System.out.println("error while parsing actors: " + e);
		}

		return tv_programs;

	}

	/**
	 * @param tvProgramFilePath
	 *            path to the program creators .tsv file
	 * @param limit
	 *            limits the number of program creators parsed (-1 for
	 *            unlimited)
	 * 
	 * @return the list of all parsed program creators
	 */
	public static tvProgramCreators Parse_tv_program_creators(
			String programCreatorsfilePath, int limit) {
		String line = null;

		// tv program creators related members
		String name;
		String id;
		String programs_created;

		tvProgramCreators program_creators = new tvProgramCreators();
		try {
			BufferedReader bi = new BufferedReader(new FileReader(
					programCreatorsfilePath));
			bi.readLine(); // skip id definition
			while ((line = bi.readLine()) != null) {

				String[] parts = line.split(FBUtils.FB_seperator, -1);

				name = parts[0];
				id = parts[1];
				programs_created = parts[2];

				program_creators.addProgramCreator(new tvProgramCreator(name,
						id, programs_created));

			}

		} catch (Exception e) {
			System.out.println("error while parsing actor program creators: "
					+ e);
		}
		return program_creators;
	}

	/**
	 * @param tvGenresFilePath
	 *            path to the program creators .tsv file
	 * @param limit
	 *            limits the number of program creators parsed (-1 for
	 *            unlimited)
	 * 
	 * @return the list of all parsed program creators
	 */
	public static tvGenres Parse_tv_genres(String tvGenresFilePath, int limit) {
		String line = null;

		// tv genre related members
		String name;
		String id;
		String programs;

		tvGenres tv_genres = new tvGenres();

		try {
			BufferedReader bi = new BufferedReader(new FileReader(
					tvGenresFilePath));
			bi.readLine(); // skip id definition
			while ((line = bi.readLine()) != null) {

				String[] parts = line.split(FBUtils.FB_seperator, -1);

				name = parts[0];
				id = parts[1];
				programs = parts[2];

				tv_genres.addGenre(new tvGenre(name, id, programs));

			}

		} catch (Exception e) {
			System.out.println("error while parsing actor program creators: "
					+ e);
		}
		return tv_genres;
	}

	/**
	 * @param filmsFilePath
	 *            path to the films .tsv file
	 * @param limit
	 *            limits the number of programs parsed (-1 for unlimited)
	 * 
	 * @return the list of all parsed films
	 */
	public static Films Parse_films(String filmsFilePath, int limit) {
		int limitCounter = 0;
		String line = null;
		Films films = new Films();

		Film film = null;

		if (filmsFilePath == null) {
			filmsFilePath = FBUtils.GetFILMFilePathFromID("film");
		}

		// film related members
		String name;
		String id;
		String initial_release_date;
		String directed_by;
		String written_by;
		String estimated_budget;
		String starring;
		String genre;
		String sequel;
		String prequel;

		try {
			BufferedReader bi = new BufferedReader(
					new FileReader(filmsFilePath));
			bi.readLine(); // skip id definition
			while ((line = bi.readLine()) != null) {
				if (limitCounter == limit) {
					break;
				}

				String[] parts = line.split(FBUtils.FB_seperator, -1);

				name = parts[0];
				id = parts[1];
				initial_release_date = parts[2];
				directed_by = parts[3];
				written_by = parts[5];
				starring = parts[13];
				genre = parts[19];
				sequel = parts[22];
				prequel = parts[23];

				film = new Film(name, id, initial_release_date, directed_by,
						written_by, "", starring, genre, sequel, prequel);

				HandleEstimatedBudget(film, parts[11]);

				films.addFilm(film);

				limitCounter++;
			}

		} catch (Exception e) {
			System.out.println("error while parsing films: " + e);
		}

		if (!budgetMap.isEmpty()) {
			Parse_dated_money_value(films);
		}

		// fix budgets
		for (Film curr_film : films.getFilms()) {

			if (curr_film.getFilm_budget() == null) {
				estimated_budget = "unavailable";
			} else {
				estimated_budget = curr_film.getFilm_budget().getBudgetValue();
			}

			curr_film.setEstimated_budget(estimated_budget);
		}

		return films;

	}

	private static void Parse_dated_money_value(Films films) {
		String line = null;

		// Parse_dated_money_value related members
		String id;
		String amount;
		String currency;
		String valid_date;
		String source;

		FilmBudget film_budget = null;

		String dated_money_value_path = FBUtils
				.GetMeasurementUnitFilePath("money_value");

		try {
			BufferedReader bi = new BufferedReader(new FileReader(
					dated_money_value_path));
			bi.readLine(); // skip id definition
			while ((line = bi.readLine()) != null) {

				String[] parts = line.split(FBUtils.FB_seperator, -1);

				id = parts[1];
				valid_date = parts[2];
				amount = parts[3];
				currency = parts[4];
				source = parts[5];

				if (budgetMap.get(id) != null) {
					film_budget = new FilmBudget(amount, currency, valid_date,
							source);

					budgetMap.get(id).init(film_budget);
				}

			}

		} catch (Exception e) {
			System.out.println("error while parsing dated money value : " + e);
		}

	}

	private static void HandleEstimatedBudget(Film film, String budgetMid) {
		if (budgetMid.length() == 0)
			return;

		FilmBudget film_budget = new FilmBudget();
		film.setFilm_budget(film_budget);
		budgetMap.put(budgetMid, film_budget);

	}

	/**
	 * @param filmActorfilePath
	 *            path to the film actor .tsv file
	 * @param limit
	 *            limits the number of actors parsed (-1 for unlimited)
	 * 
	 * @return the list of all parsed film actors
	 */
	public static filmActors Parse_film_actors(String filmActorfilePath,
			int limit) {

		int limitCounter = 0;
		String line = null;
		filmActors film_actors = new filmActors();

		// film actor related members
		String actor_id = null;
		String name = null;
		String film_roles = null;
		try {
			BufferedReader bi = new BufferedReader(new FileReader(
					filmActorfilePath));
			bi.readLine(); // skip id definition
			while ((line = bi.readLine()) != null) {
				if (limitCounter == limit) {
					break;
				}

				String[] parts = line.split(FBUtils.FB_seperator, -1);

				name = parts[0];
				actor_id = parts[1];
				film_roles = parts[2];

				film_actors.addActor(new filmActor(actor_id, name, film_roles));

				limitCounter++;
			}

		} catch (Exception e) {
			System.out.println("error while parsing film actors : " + e);
		}

		return film_actors;

	}

	/**
	 * @param filmActorfilePath
	 *            path to the film directors .tsv file
	 * @param limit
	 *            limits the number of directors parsed (-1 for unlimited)
	 * 
	 * @return the list of all parsed film directors
	 */
	public static filmDirectors Parse_film_directors(
			String filmDirectosFilePath, int limit) {

		int limitCounter = 0;
		String line = null;
		filmDirectors film_directors = new filmDirectors();

		// film actor related members
		String id = null;
		String name = null;
		String films = null;
		try {
			BufferedReader bi = new BufferedReader(new FileReader(
					filmDirectosFilePath));
			bi.readLine(); // skip id definition
			while ((line = bi.readLine()) != null) {
				if (limitCounter == limit) {
					break;
				}

				String[] parts = line.split(FBUtils.FB_seperator, -1);

				name = parts[0];
				id = parts[1];
				films = parts[2];

				film_directors.addDirector(new filmDirector(name, id, films));

				limitCounter++;
			}

		} catch (Exception e) {
			System.out.println("error while parsing film directors : " + e);
		}

		return film_directors;

	}

	/**
	 * @param filmWritersFilePath
	 *            path to the film writers .tsv file
	 * @param limit
	 *            limits the number of writers parsed (-1 for unlimited)
	 * 
	 * @return the list of all parsed film writers
	 */
	public static filmWriters Parse_film_writers(String filmWritersFilePath,
			int limit) {

		int limitCounter = 0;
		String line = null;
		filmWriters film_writers = new filmWriters();

		// film actor related members
		String id = null;
		String name = null;
		String films = null;
		try {
			BufferedReader bi = new BufferedReader(new FileReader(
					filmWritersFilePath));
			bi.readLine(); // skip id definition
			while ((line = bi.readLine()) != null) {
				if (limitCounter == limit) {
					break;
				}

				String[] parts = line.split(FBUtils.FB_seperator, -1);

				name = parts[0];
				id = parts[1];
				films = parts[2];

				film_writers.addWriter(new filmWriter(name, id, films));

				limitCounter++;
			}

		} catch (Exception e) {
			System.out.println("error while parsing film writers : " + e);
		}

		return film_writers;

	}

	/**
	 * @param filmRolesFilePath
	 *            path to the film roles .tsv file
	 * @param limit
	 *            limits the number of roles parsed (-1 for unlimited)
	 * 
	 * @return the list of all parsed film roles
	 */
	public static filmRoles Parse_film_roles(String filmRolesFilePath, int limit) {
		String line = null;

		// film role related members
		String id;
		String actor;
		String film;
		String character;

		filmRoles film_roles = new filmRoles();

		try {
			BufferedReader bi = new BufferedReader(new FileReader(
					filmRolesFilePath));
			bi.readLine(); // skip id definition
			while ((line = bi.readLine()) != null) {

				String[] parts = line.split(FBUtils.FB_seperator, -1);

				id = parts[1];
				actor = parts[2];
				film = parts[3];
				character = parts[5];

				film_roles
						.addFilmRole(new filmRole(id, actor, film, character));

			}

		} catch (Exception e) {
			System.out.println("error while parsing actor film roles: " + e);
		}

		return film_roles;
	}

	
	/**
	 * @param tvGenresFilePath
	 *            path to the film genres .tsv file
	 * @param limit
	 *            limits the number of film genres parsed (-1 for
	 *            unlimited)
	 * 
	 * @return the list of all parsed film genres
	 */
	public static filmGenres Parse_film_genres(String filmGenresFilePath, int limit) {
		String line = null;

		// films genre related members
		String name;
		String id;
		String films;

		filmGenres film_genres = new filmGenres();

		try {
			BufferedReader bi = new BufferedReader(new FileReader(
					filmGenresFilePath));
			bi.readLine(); // skip id definition
			while ((line = bi.readLine()) != null) {

				String[] parts = line.split(FBUtils.FB_seperator, -1);

				name = parts[0];
				id = parts[1];
				films = parts[2];

				film_genres.addGenre(new filmGenre(name, id, films));

			}

		} catch (Exception e) {
			System.out.println("error while parsing actor program creators: "
					+ e);
		}
		return film_genres;
	}
	
}
